#include <sxMassSpringSimulationReplayController.h>

Sx::Simulation::Interface::MassSpringSimulationReplayController::MassSpringSimulationReplayController(Viewport* viewport, const std::shared_ptr<Canvas>& canvas, const std::shared_ptr<ScenePickingController>& pickingController, const std::shared_ptr<RendererImplementation>& cameraRenderer, const std::shared_ptr<MassSpringSimulation>& simulation, const std::shared_ptr<MassSpringSimulationRecording>& recording) :
	MassSpringSimulationController(viewport, canvas, pickingController, cameraRenderer, simulation), recording(nullptr) {

	this->paused = true;
	this->recording = recording;
}

Sx::Simulation::Interface::MassSpringSimulationReplayController::~MassSpringSimulationReplayController() {
	/* this->simulation not owned by this class */
	this->timelines.clear();
}

void Sx::Simulation::Interface::MassSpringSimulationReplayController::update(float dt) {
	if ( this->recording == nullptr ) return;
	if ( this->paused == false ) this->setCurrentFrame(this->frameIndex);

	// If the this replay controller has played all of the available frames,
	// then pause it.
	if ( this->frameIndex >= this->recording->getFrameCount() ) this->pause();

	//--------------------------------------------------------------------------
	// If there are any timelines listening to the simulation then update them.
	//--------------------------------------------------------------------------
	if ( this->paused == true ) return;

	if ( this->frameIndex <= this->recording->getFrameCount() ) {
		for ( unsigned int i = 0; i < this->timelines.size(); i++ ) {
			if ( this->timelines[i] == nullptr ) continue;
			this->timelines[i]->setCurrentTime(this->frameIndex);
		}
	}
}

void Sx::Simulation::Interface::MassSpringSimulationReplayController::render() const {
	if ( this->simulation == nullptr ) return;
	// TODO: make sure that this renders at the recordings fps not whatever the
	// viewports default is

	ViewController::preRender();
		this->simulation->render();
	ViewController::postRender();
}

void Sx::Simulation::Interface::MassSpringSimulationReplayController::setFrameIndex(unsigned int index) {
	MassSpringSimulationController::setFrameIndex(index);
	this->setCurrentFrame(index);
}

void Sx::Simulation::Interface::MassSpringSimulationReplayController::incrementFrameIndex() {
	this->setCurrentFrame(this->frameIndex + 1);
}

void Sx::Simulation::Interface::MassSpringSimulationReplayController::decrementFrameIndex() {
	if ( this->frameIndex - 1 < 0 ) return;
	this->setCurrentFrame(this->frameIndex - 1);
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::addTimeline(const std::shared_ptr<FixedTimeline>& timeline) {
	if ( timeline == nullptr ) {
		std::cerr << "[MassSpringSimulationReplayController:addTimeline] Error: Cannot add nullptr timeline to abstract simulation." << std::endl;
		return false;
	}

	this->timelines.add(timeline);
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::removeTimeline(const std::shared_ptr<FixedTimeline>& timeline) {
	if ( timeline == nullptr ) {
		std::cerr << "[MassSpringSimulationReplayController:removeTimeline] Error: Cannot remove nullptr timeline from abstract simulation." << std::endl;
		return false;
	}

	this->timelines.remove(timeline);
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::setCurrentFrame(unsigned int index) {
	if ( this->recording == nullptr ) return false;
	if ( this->recording.use_count() == 0 ) return false;

	// Based on the internal recording reference, load the data from the
	// desired frame
	MassSpringSimulationKeyFrame frame;
	bool validFrame = this->recording->getFrame(index, frame);

	// This is the flag that the recording doesn't have a stored frame.. return control to the physic engine..
	if ( validFrame == false ) return false;
	this->frameIndex = index;

	MassSpringMeshState state;
	bool validState = false;
	for ( unsigned int i = 0; i < frame.meshStates.size(); i++ ) {
		validState = frame.meshStates.get(i, state);
		if ( validState == false ) return false; // just for now.. a branch is too slow for this code
		state.load(); // must have same valid mass spring simulation.... :(
	}

	// If there are any timelines listening to this event set their current
	// index to match the frame index.
	for ( unsigned int i = 0; i < this->timelines.size(); i++ ) {
		if ( this->timelines[i] == nullptr ) continue;
		this->timelines[i]->setCurrentTime(index);
	}

	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::play() {
	this->paused = false;
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::stop() {
	this->paused = true;
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::pause() {
	this->paused = true;
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::resume() {
	this->paused = false;
	return true;
}

bool Sx::Simulation::Interface::MassSpringSimulationReplayController::isPaused() {
	return this->paused;
}

unsigned int Sx::Simulation::Interface::MassSpringSimulationReplayController::size() const {
	return this->recording->length();
}
